package org.lex.swing.widgets.console;

import java.awt.Frame;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.List;
import java.util.Vector;

public class AWTConsole extends AbstractAWTConsole {
	private static final long serialVersionUID = 3439514555466895103L;
	private StringBuilder buffer = new StringBuilder();
	private String startedLine;
	private int textLength = 0;
	private List<String> history = new Vector<String>();
	private int histLine = 0;

	public static void main(String args[]) {
		AWTConsole console = new AWTConsole();
		console.addConsoleListener(new ConsoleListener() {
			public void execute(String command) {
				System.out.println("typed command: [" + command + "]");
			}
		});
		final Frame f = new Frame("AWT Console");
		f.add(console, "Center");
		f.pack();
		f.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				f.dispose();
			}
		});
		f.setVisible(true);
	}

	public AWTConsole() {
		this(25, 80);
	}

	public AWTConsole(int rows, int cols) {
		// initialize
		super(rows, cols);
		this.setEditable(false);
		this.addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent e) {
				processKeyPressed(e.getKeyCode(), e.getKeyChar(), e.getModifiers());
				e.consume();
			}
		});
		this.requestFocus();
	}

	public synchronized AWTConsole print(Object o) {
		this.append(String.valueOf(o));
		textLength = this.getText().length(); // sync for safety
		return this;
	}

	public void clear() {
		this.setText("");
		this.textLength = 0;

	}

	public String toString() {
		return "AWTConsole";
	}

	private void processKeyPressed(int code, char ch, int modifiers) {
		switch (code) {
		case (KeyEvent.VK_BACK_SPACE):
			if (buffer.length() > 0) {
				buffer.setLength(buffer.length() - 1);
				this.replaceRange("", textLength - 1, textLength);
				textLength--;
			}
			break;
		case (KeyEvent.VK_ENTER):
			enterPressed();
			break;
		case (KeyEvent.VK_U):
			if ((modifiers & InputEvent.CTRL_MASK) > 0) {
				int len = buffer.length();
				this.replaceRange("", textLength - len, textLength);
				buffer.setLength(0);
				histLine = 0;
				textLength = this.getText().length();
			} else
				doChar(ch);
			break;
		case (KeyEvent.VK_UP):
			historyUp();
			break;
		case (KeyEvent.VK_DOWN):
			historyDown();
			break;
		case (KeyEvent.VK_TAB):
			buffer.append("    ");
			this.append("    ");
			textLength += 4;
			break;
		case (KeyEvent.VK_C):
			if ((modifiers & InputEvent.CTRL_MASK) > 0) {
				buffer.append("^C");
				this.append("^C");
				textLength += 2;
			} else
				doChar(ch);
			break;
		default:
			doChar(ch);
		}
	}

	private void doChar(char ch) {
		if ((ch >= ' ') && (ch <= '~')) {
			buffer.append(ch);
			this.append(String.valueOf(ch));
			textLength++;
		}
	}

	private void enterPressed() {
		String s = buffer.toString();
		if (buffer.length() > 0) {
			history.add(s);
		}
		buffer.setLength(0);
		histLine = 0;
		this.append("\n");
		textLength = this.getText().length(); // sync for safety
		this.setCaretPosition(textLength);
		this.fireConsoleExecute(s);
	}

	private void historyUp() {
		if (history.size() == 0)
			return;
		if (histLine == 0) // save current line
			startedLine = buffer.toString();
		if (histLine < history.size()) {
			histLine++;
			showHistoryLine();
		}
	}

	private void historyDown() {
		if (histLine == 0)
			return;
		histLine--;
		showHistoryLine();
	}

	private void showHistoryLine() {
		String showline;
		if (histLine == 0)
			showline = startedLine;
		else
			showline = (String) history.get(history.size() - histLine);
		this.replaceRange(showline, textLength - buffer.length(), textLength);
		buffer = new StringBuilder(showline);
		textLength = this.getText().length();
	}
}
